package com.fone.player.airone;

import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
import java.util.Timer;
import java.util.Vector;
import java.util.concurrent.ConcurrentMap;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import javax.jmdns.impl.JmDNSImpl;
import javax.jmdns.impl.JmDNSImpl.ServiceCollector;

import nano.HelloServer;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

import com.fone.player.util.L;

import dlna.CDLNAPlayerListener;

/**
 * airone service 提供dlna 相关服务，移植于3.4
 * 
 * @author kongxiaojun
 * @since 2014-4-24
 */
public class AironeService extends Service {

	public static final String ACTION = "Fone.AIRONE";
	private JmDNS jmdns;
	private final static String SCAN_TYPE = "_airplay._tcp.local.";
	public static String TAG = "AIRONE";
	public static String TYPE = "ADD";
	public static int PORT = 9999;
	private boolean started;
	static HelloServer http_server;

	Vector<DeviceUpdate> last_dlnas = new Vector<DeviceUpdate>();

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		new Thread() {
			public void run() {
				to_start_http();
			}
		}.start();
	}

	private IAironeService.Stub mBinder = new IAironeService.Stub() {
		@Override
		public void start() throws RemoteException {

			try {
				to_stop_scan_appletv();
				FDLNAPlay.stop();
				started = false;

			} catch (Exception e1) {
				e1.printStackTrace();
				L.i(TAG, "", "AppleTV" + e1);
			}

			started = true;
			new Thread() {
				public void run() {
					to_scan_appletv();
				}
			}.start();
			new Thread() {
				public void run() {
					to_scan_dlna();
				}
			}.start();

		}

		@Override
		public void stop() throws RemoteException {

			to_stop_scan_appletv();
			FDLNAPlay.stop();
			started = false;

		}

		@Override
		public boolean isStarted() throws RemoteException {
			return started;
		}

		@Override
		public int dlna_start(String uuid, double start_per) throws RemoteException {
			return FDLNAPlay.RS_OK;
		}

		@Override
		public int dlna_pause(String uuid) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).playpause(uuid, true) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;
		}

		@Override
		public int dlna_resume(String uuid) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).playpause(uuid, false) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;
		}

		@Override
		public int dlna_seek(String uuid, int time) throws RemoteException {
			time *= 1000;
			if (time < 0) {
				time = 0;
			}
			long Hours = time / 3600000;
			long Minutes = (time / 60000) % 60;
			long Seconds = (time / 1000) % 60;
			String formatTime = String.format("%1$02d:%2$02d:%3$02d", Hours, Minutes, Seconds);
			return FDLNAPlay.getDLNA(getApplicationContext()).seek(uuid, formatTime) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;

		}

		@Override
		public int dlna_stop(String uuid) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).stop(uuid) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;
		}

		@Override
		public int dlna_set_volume(String uuid, int volume) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).setplayvolume(uuid, volume) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;
		}

		@Override
		public int dlna_get_play_state(String uuid) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).getPlayState(uuid);
		}

		@Override
		public int dlna_get_play_volume(String uuid) throws RemoteException {
			return FDLNAPlay.getDLNA(getApplicationContext()).getplayvolume(uuid);
		}

		@Override
		public int dlna_get_duration(String uuid) throws RemoteException {
			String duration = FDLNAPlay.getDLNA(getApplicationContext()).getduration(uuid);
			return get60Unit(duration);
		}

		@Override
		public int dlna_get_play_curtime(String uuid) throws RemoteException {
			String time = FDLNAPlay.getDLNA(getApplicationContext()).getplaycurtime(uuid);
			return get60Unit(time);
		}

		@Override
		public void reSearchDevs() throws RemoteException {
			new Thread() {
				public void run() {
					to_scan_appletv();
				}
			}.start();
		}

		@Override
		public int dlna_open(String uuid, String file, String title, String icon, int pos) throws RemoteException {
			L.v(TAG, "uuid==" + uuid + "  file==" + file + "  title==" + title + "  icon==" + icon + "  pos==" + pos);
			try {
				return FDLNAPlay.getDLNA(getApplicationContext()).play(file, uuid, title, icon, pos) ? FDLNAPlay.RS_OK : FDLNAPlay.RS_ERROR;
			} catch (Exception e) {
				return FDLNAPlay.RS_ERROR;
			}
		}

		@Override
		public boolean loginTvDevice(String devuuid, String sid, String nickname, String otherinfo) throws RemoteException {
			// TODO Auto-generated method stub
			try {
				return FDLNAPlay.getDLNA(getApplicationContext()).loginTvDevice(devuuid, sid, nickname, otherinfo);
			} catch (Exception e) {
				return false;
			}
		}

		@Override
		public String getDeviceManufactur(String devuuid) throws RemoteException {
			// TODO Auto-generated method stub
			try {
				return FDLNAPlay.getDLNA(getApplicationContext()).getDeviceManufactur(devuuid);
			} catch (Exception e) {
				return null;
			}
		}

	};

	private static int get60Unit(String duration) {
		try {
			int t = 1;
			int seconds = 0;
			String[] times = duration.split(":");
			for (int i = times.length - 1; i > -1; i--) {
				seconds += Integer.parseInt(times[i]) * t;
				t *= 60;
			}
			return seconds;
		} catch (Exception e) {
			L.e(TAG, "airone", "error time string:" + duration);
			// Log.e("airone", "error time string:" + duration);
			return 0;
		}

	}

	public void onDestroy() {
		to_stop_scan_appletv();
		FDLNAPlay.getDLNA(getApplicationContext()).Stop();
		started = false;
		super.onDestroy();
	};

	/**
	 * start http server to dlna device request this device's local file
	 * 
	 * @return void
	 * @throws
	 */
	private void to_start_http() {
		try {
			to_stop_http();
		} catch (Exception e) {

		}
		try {
			http_server = new HelloServer(PORT);
			L.i("airone", "", "http sever started");
		} catch (IOException e) {
			e.printStackTrace();
			L.e("airone", "", "http sever start error:" + e.toString());
		}
	}

	/**
	 * stop http server
	 * 
	 * @return void
	 * @throws
	 */
	private void to_stop_http() {
		if (http_server != null)
			http_server.stop();
		http_server = null;
	}

	/**
	 * dlna device has changed
	 * 
	 * @param dev
	 *            device
	 * @param add
	 *            add or remove
	 * @return void
	 * @throws
	 */
	public void onDevicesChanged(AirServerDevice dev, boolean add) {

		L.v(TAG, "onDevicesChanged" + dev.toString());

		Intent intent = new Intent();
		intent.setAction(ACTION);
		intent.putExtra(TAG, dev);
		intent.putExtra(TYPE, add);
		sendBroadcast(intent);

	}

	/**
	 * stop search dlna device
	 * 
	 * @return void
	 * @throws
	 */
	private void to_stop_scan_appletv() {
		if (jmdns_timer != null) {
			jmdns_timer.cancel();
			jmdns_timer = null;
		}
		if (jmdns != null) {
			try {
				jmdns.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			jmdns = null;
		}
	}

	Timer jmdns_timer;
	boolean scaning;

	/**
	 * search dlna device
	 * 
	 * @return void
	 * @throws
	 */
	private void to_scan_appletv() {
		if (scaning)
			return;
		scaning = true;
		// for (int i = 0; i < 30; i++) {
		// toscan();
		// }
		startAppleScanListener();
		scaning = false;

	}

	/**
	 * search dlna device
	 * 
	 * @return
	 * @return boolean
	 * @throws
	 */
	private boolean toscan() {
		InetAddress address = null;
		try {
			// Enumeration<NetworkInterface> ifc =
			// NetworkInterface.getNetworkInterfaces();
			// while (ifc.hasMoreElements()) {
			// NetworkInterface anInterface = ifc.nextElement();
			//
			// Enumeration<InetAddress> addr = anInterface.getInetAddresses();
			// while (addr.hasMoreElements()) {
			// if (!(address = addr.nextElement()).isLoopbackAddress())
			// break;
			// }
			//
			// }
			// Log.i(TAG, "AppleTV to Scan ..");
			// if( address != null )
			// jmdns = JmDNS.create(address);
			// else
			jmdns = JmDNS.create();
			L.i("airone", "", "scan appletv");
			ServiceInfo[] list = jmdns.list(SCAN_TYPE, 20000);
			for (ServiceInfo info : list) {
				AirServerDevice apple_tv = new AirServerDevice();
				apple_tv.proto = 0;
				L.v("airone", "info name : " + info.getName());
				if (info.getInet4Addresses().length > 0) {
					if (info.getInet4Addresses()[0] == null) {
						continue;
					}
					apple_tv.server_ip_addr = info.getInet4Addresses()[0].getHostAddress();
					apple_tv.server_name = info.getName();
					apple_tv.server_port = info.getPort();
					apple_tv.server_uid = info.getPropertyString("deviceid");
					L.i("airone", "", "scan appletv find one:" + apple_tv.server_name);
					onDevicesChanged(apple_tv, true);

				}
			}
			if (jmdns != null)
				jmdns.close();
			jmdns = null;
			if (list.length == 0)
				return false;
			else
				return true;

		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

	}

	private void startAppleScanListener() {

		L.v(TAG, "airone startAppleScanListener");

		try {
			jmdns = JmDNS.create();
		} catch (IOException e) {
			e.printStackTrace();
		}
		final ServiceListener slistener = new ServiceListener() {

			@Override
			public void serviceResolved(ServiceEvent ev) {
				L.i(TAG, "", "airone AppleTV serviceResolved " + ev.getInfo().getName());
				addAppleTv(ev);
			}

			@Override
			public void serviceRemoved(ServiceEvent ev) {
				L.i(TAG, "", "AppleTV serviceRemoved type:" + ev.toString());
				ServiceInfo info = ev.getInfo();
				AirServerDevice apple_tv = new AirServerDevice();
				apple_tv.proto = 0;
				apple_tv.server_uid = info.getPropertyString("deviceid");
				onDevicesChanged(apple_tv, false);

			}

			@Override
			public void serviceAdded(ServiceEvent event) {
				// jmdns.requestServiceInfo(event.getType(), event.getName(),
				// 5000);
				addAppleTv(event);
			}
		};

		if (jmdns != null) {
			jmdns.addServiceListener(SCAN_TYPE, slistener);
		}
	}

	/**
	 * 添加airplay 设备到设备列表
	 * 
	 * @param event
	 * @return void
	 * @throws
	 */
	private void addAppleTv(ServiceEvent event) {
		L.i(TAG, "", "airone AppleTV serviceAdded " + event.getInfo().getName());
		ServiceInfo info = event.getInfo();
		if (info.getInet4Addresses().length <= 0) {
			info = event.getDNS().getServiceInfo(info.getType(), info.getName(), 2000);
			// JmDNSImpl impl = (JmDNSImpl) event.getSource();
			// ConcurrentMap<String, ServiceCollector> map =
			// impl.get_serviceCollectors();
			// ServiceCollector collector = map.get(SCAN_TYPE);
			// if (collector != null) {
			// L.v(TAG, "serviceAdded event " + collector.toString());
			// ConcurrentMap<String, ServiceInfo> infomap =
			// collector.get_infos();
			// if (infomap != null) {
			// ServiceInfo infoNew = infomap.get(info.getName());
			// if (infoNew != null) {
			// info = infoNew;
			// L.v(TAG, "airone serviceAdded event " +
			// infoNew.getInet4Addresses()[0].getHostAddress());
			// }
			// }
			// }
		}
		if (info.getInet4Addresses().length <= 0) {
			return;
		}
		AirServerDevice apple_tv = new AirServerDevice();
		apple_tv.proto = 0;
		apple_tv.server_ip_addr = info.getInet4Addresses()[0].getHostAddress();
		apple_tv.server_name = info.getName();
		apple_tv.server_port = info.getPort();
		apple_tv.server_uid = info.getPropertyString("deviceid");
		onDevicesChanged(apple_tv, true);
	}

	private void to_scan_dlna() {
		try {
			FDLNAPlay.getDLNA(getApplicationContext()).Start();
			// L.i(TAG,"","dlna_address:"+FDLNAPlay.getDLNA(getApplicationContext()));
			FDLNAPlay.getDLNA(getApplicationContext()).setdeviceListener(new CDLNAPlayerListener() {

				@Override
				public void deviceRemoved(String arg0) {
					refreshDlna();
				}

				@Override
				public void deviceAdded(String arg0) {
					refreshDlna();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
			L.e(TAG, "", "can't start DLNAPlayer...");
		}

	}

	private void refreshDlna() {
		L.i(TAG, "refreshDlna ");
		List<String> list = FDLNAPlay.getDLNA(getApplicationContext()).getAvailableDevices();
		L.i(TAG, "refreshDlna list =" + list);

		if (list != null)
			synchronized (list) {
				for (String uuid : list) {
					boolean isequle = false;
					for (DeviceUpdate du : last_dlnas) {
						if ((du == null || TextUtils.isEmpty(du.uuid) || TextUtils.isEmpty(du.name)) || (du.uuid.contains(uuid) && du.name.equals(FDLNAPlay.getDLNA(getApplicationContext()).getDeviceName(uuid)))) {
							isequle = true;
							break;
						}
					}
					L.i(TAG, "refreshDlna isequle =" + isequle);
					if (isequle) {
						continue;
					} else {
						AirServerDevice at = new AirServerDevice();
						at.server_name = FDLNAPlay.getDLNA(getApplicationContext()).getDeviceName(uuid);
						L.i(TAG, "refreshDlna server_name =" + at.server_name);
						at.server_uid = uuid;
						at.proto = 1;
						at.server_ip_addr = FDLNAPlay.getDLNA(getApplicationContext()).getDeviceIP(uuid);
						onDevicesChanged(at, true);
					}
				}
				for (DeviceUpdate du : last_dlnas) {
					Log.i("chao", "refreshDlna (!list.contains(uuid) =" + (!list.contains(du.uuid)));
					if (du != null && !TextUtils.isEmpty(du.uuid) && !list.contains(du.uuid)) {
						AirServerDevice at = new AirServerDevice();
						at.server_uid = du.uuid;
						at.proto = 1;
						onDevicesChanged(at, false);
					}
				}
				last_dlnas.clear();
				for (String uuid : list) {
					if (!TextUtils.isEmpty(uuid)) {
						DeviceUpdate du = new DeviceUpdate();
						du.uuid = uuid;
						du.name = FDLNAPlay.getDLNA(getApplicationContext()).getDeviceName(uuid);
						last_dlnas.add(du);
					}
				}
			}
		L.i(TAG, "refreshDlna last_dlnas =" + last_dlnas);
	}

	public class DeviceUpdate {
		public String uuid;
		public String name;
	}

}
